iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 10
1

來開始實作Dynamic Caching吧 /images/emoticon/emoticon43.gif

到目前為止,我們已經在在install階段中實現了pre-cache靜態資源,現在讓我們也來實作Dynamic Caching。首先動態暫存意味著仍然會發出一個fetch event,我們只需要將返回的response暫存到cache中。所以要做到這一點,我們必須要修改一下sw.js中fetch event lister的回傳結果。

回顧一下,我們目前的策略是會先到cache中尋找是否有我們「預先暫存好」的資源,若有(也就是response不為null)則直接回傳cache中的值,沒有的話則必須要透過fetch API取得該資源後再回傳。

self.addEventListener('fetch', function(event) {
    event.respondWith(
        caches.match(event.request).then(function(response) {
            if(response) {
                return response;
            } else {
                return fetch(event.request);
            }
});

現在應該要改成當沒有在cache中的話,一樣透過fetch API去請求外部資源,只不過將回傳的結果再做進一步的處理。我先再創建一個sub-cache名叫「dynamic」(說明是要暫存動態資源的部分),然後用cache.put()將「request (key)」和「回傳的response (value)」暫存到dynamic sub-cache中。

來看一下要怎寫吧:

self.addEventListener('fetch', function(event) {
    event.respondWith(
        caches.match(event.request).then(function(response) {
            if(response) {
                return response;
            } else {
                return fetch(event.request).then(function(res) {
                    return caches.open('dynamic').then(function(cache) {
                        cache.put(event.request.url, res.clone());
                        return res;
                    })
                }).catch(function(err) {
                    
                });
            }
        })
    );
});

這裡有幾點要特別明:

  • 剛剛提到的動態資源是指「不是固定」且「不斷變動」的資源,有可能是當用戶訪問時才會去獲取的
  • cache.put()和之前說用到的cache.add()有一些不同,add方法會自動會發出request(也就是我們帶進去的參數),並將response加入cache中。而put方法則是只負責將我們輸入的兩個參數(也就是request和response)加入到cache中,它並不會真正地對外發出request。
  • 在put方法的第二個參數,我不直接使用res而是res.clone()的原因是response object只能被使用一次,也就是說我如果在cache.put使用res的話,下一行要return res時,是回傳一個空值。為了避免這種情形我使用response object內建的clone方法複製response。

目前還有一個問題/images/emoticon/emoticon02.gif

讓我們來看一下如果我今天修改feed.js中的createCard()將title顏色設為白色,看一下畫面結果:

cardTitleTextElement.style.color = 'white';

哇!!! 沒有改變 ... WHY??

原因是我們雖然修改了js file,但是service worker並沒有更新,所以還是繼續拿原本static cache中的feed.js來執行。最常見的解決發案是每次修該相關的靜態檔案時,都要更新sub-cache的名稱(也就是增加新的版本號),並把舊版本的sub-cache清除。

那清除舊版本sub-cache的code應該寫在哪?較好的地方是在service worker的activate階段,因為只有當在用戶關閉所有頁面,並打開新的應用程式後才會執行此操作,所以現在更新cache才是安全的(由於用戶已經不在運行的應用程式中了)。

來看要怎麼清除cache吧/images/emoticon/emoticon13.gif

var CACHE_STATIC_NAME = 'static-v4';
var CACHE_DYNAMIC_NAME = 'dynamic-v2';

self.addEventListener('activate', function(event) {
    console.log('[Service Worker] Activating Service Worker ...', event);
    event.waitUntil(
        caches.keys().then(function(keyList) {
            return Promise.all(keyList.map(function(key) {
                if(key !== CACHE_STATIC_NAME && key !== CACHE_DYNAMIC_NAME) {
                    console.log('[Service Worker] Removing old cache.', key);
                    return caches.delete(key);
                }
            }));
        })
    );
    return self.clients.claim();
});

說明一下,caches.keys()會回傳一個「sub-cache名稱所形成的字串陣列」,之後我將移除的部分寫在Promise.all()裡面,確保全部執行完清除cache的邏輯後才會回傳。

不過由於Promise.all()裡面必須是promise object,所以我必須將keyList透過js原生的map()函式把「每個key值(也就是sub-cache名稱)」對應到「清除cache的function」來執行我要的判斷。

今天先到這兒囉~~

Day10 結束!! /images/emoticon/emoticon29.gif


上一篇
[Day09] 在Service Worker中實作Caching(Part2)
下一篇
[Day11] Service Worker進階Caching實作(Part1)
系列文
你應該要知道的新一代Web技術---漸進式網頁(PWA)29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言